/*! \file
 * \brief Enter a brief description here.
 *
 * Enter a detailed description here. (optional)
 * \author Author (Ivo Wingelaar)
 * \date Date (2011)
 * \version Version (0.0.1)
 * \copyright Copyright (GNU LGPL v3)
 */

#include "LynxExtensionHandler.hpp"
#include <stdio.h>

namespace Lynx
{

OpenGLExtensionHandler::OpenGLExtensionHandler(LogManager* log) :
    mStencilBuffer(false),
    mMultiTextureExtension(false),
    mTextureCompressionExtension(false),
    mMaxTextureUnits(1),
    mMaxLights(1),
    mMaxAnisotropy(1),
    mMaxUserClipPlanes(0),
    mMaxAuxBuffers(0),
    mMaxMultipleRenderTargets(1),
    mMaxIndices(65535),
    mMaxTextureSize(1),
    mMaxGeometryVerticesOut(0),
    mMaxTextureLODBias(0.f),
    mVersion(0),
    mShaderLanguageVersion(0),
    pGlActiveTextureARB(0),
    pGlClientActiveTextureARB(0),
    pGlGenProgramsARB(0),
    pGlGenProgramsNV(0),
    pGlBindProgramARB(0),
    pGlBindProgramNV(0),
    pGlDeleteProgramsARB(0),
    pGlDeleteProgramsNV(0),
    pGlProgramStringARB(0),
    pGlLoadProgramNV(0),
    pGlProgramLocalParameter4fvARB(0),
    pGlCreateShaderObjectARB(0),
    pGlShaderSourceARB(0),
    pGlCompileShaderARB(0),
    pGlCreateProgramObjectARB(0),
    pGlAttachObjectARB(0),
    pGlLinkProgramARB(0),
    pGlUseProgramObjectARB(0),
    pGlDeleteObjectARB(0),
    pGlGetAttachedObjectsARB(0),
    pGlGetInfoLogARB(0),
    pGlGetObjectParameterivARB(0),
    pGlGetUniformLocationARB(0),
    pGlUniform1ivARB(0),
    pGlUniform1fvARB(0),
    pGlUniform2fvARB(0),
    pGlUniform3fvARB(0),
    pGlUniform4fvARB(0),
    pGlUniformMatrix2fvARB(0),
    pGlUniformMatrix3fvARB(0),
    pGlUniformMatrix4fvARB(0),
    pGlGetActiveUniformARB(0),
    pGlPointParameterfARB(0),
    pGlPointParameterfvARB(0),
    pGlStencilFuncSeparate(0),
    pGlStencilOpSeparate(0),
    pGlStencilFuncSeparateATI(0),
    pGlStencilOpSeparateATI(0),
    pGlCompressedTexImage2D(0),
#if defined(GLX_SGI_swap_control)
    glxSwapIntervalSGI(0),
#endif
#if defined(GLX_EXT_swap_control)
    glxSwapIntervalEXT(0),
#endif
    pGlBindFramebufferEXT(0),
    pGlDeleteFramebuffersEXT(0),
    pGlGenFramebuffersEXT(0),
    pGlCheckFramebufferStatusEXT(0),
    pGlFramebufferTexture2DEXT(0),
    pGlBindRenderbufferEXT(0),
    pGlDeleteRenderbuffersEXT(0),
    pGlGenRenderbuffersEXT(0),
    pGlRenderbufferStorageEXT(0),
    pGlFramebufferRenderbufferEXT(0),
    pGlDrawBuffersARB(0),
    pGlDrawBuffersATI(0),
    pGlGenBuffersARB(0),
    pGlBindBufferARB(0),
    pGlBufferDataARB(0),
    pGlDeleteBuffersARB(0),
    pGlBufferSubDataARB(0),
    pGlGetBufferSubDataARB(0),
    pGlMapBufferARB(0),
    pGlUnmapBufferARB(0),
    pGlIsBufferARB(0),
    pGlGetBufferParameterivARB(0),
    pGlGetBufferPointervARB(0),
    pGlProvokingVertexARB(0),
    pGlProvokingVertexEXT(0),
    pGlColorMaskIndexedEXT(0),
    pGlEnableIndexedEXT(0),
    pGlDisableIndexedEXT(0),
    pGlBlendFuncIndexedAMD(0),
    pGlBlendFunciARB(0),
    pGlProgramParameteriARB(0),
    pGlProgramParameteriEXT(0),
    mLogManager(log)
{
    for(uint32_t i=0; i<LYNX_OpenGL_Feature_Count; ++i)
        mFeatureAvailable[i] = false;
    mDimAliasedLine[0]=1.f;
    mDimAliasedLine[1]=1.f;
    mDimAliasedPoint[0]=1.f;
    mDimAliasedPoint[1]=1.f;
    mDimSmoothedLine[0]=1.f;
    mDimSmoothedLine[1]=1.f;
    mDimSmoothedPoint[0]=1.f;
    mDimSmoothedPoint[1]=1.f;
}

void OpenGLExtensionHandler::dump() const
{
    for(uint32_t i=0; i < LYNX_OpenGL_Feature_Count; ++i)
        if(mFeatureAvailable[i])
            mLogManager->log(OpenGLFeatureStrings[i], mFeatureAvailable[i] ? " true" : " false");
}

void OpenGLExtensionHandler::initExtensions(bool stencilBuffer)
{
    const float ogl_ver = atof(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
    mVersion = static_cast<uint16_t>(Myth::Math::floor(ogl_ver)*100+Myth::Math::round(Myth::Math::fract(ogl_ver)*10.0f));
    if(mVersion >= 102)
    {
        std::string dvers = "OpenGL driver version is ";
        char tmp[255];
        snprintf(tmp, 255, "%d", mVersion);
        dvers += tmp;
        mLogManager->log(dvers.c_str(), ELL_INFORMATION);

    }
    else
        mLogManager->log("OpenGL driver version is not 1.2 or better.", ELL_WARNING);

    {
        const char* t = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
        size_t len = 0;
        char* str = 0;
        if(t)
        {
            len = strlen(t);
            str = new char[len+1];
        }
        char* p = str;

        for(size_t i=0; i<len; ++i)
        {
            str[i] = static_cast<char>(t[i]);

            if(str[i] == ' ')
            {
                str[i] = 0;
                for(uint32_t j=0; j < LYNX_OpenGL_Feature_Count; ++j)
                {
                    if(!strcmp(OpenGLFeatureStrings[j], p))
                    {
                        mFeatureAvailable[j] = true;
                        break;
                    }
                }

                p = p + strlen(p) + 1;
            }
        }

        delete [] str;
    }

    mMultiTextureExtension = mFeatureAvailable[LYNX_ARB_multitexture];
    mTextureCompressionExtension = mFeatureAvailable[LYNX_ARB_texture_compression];
    mStencilBuffer=stencilBuffer;

#ifdef _LYNX_WINDOWS_
    // get multitexturing function pointers
    pGlActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) wglGetProcAddress("glActiveTextureARB");
    pGlClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC) wglGetProcAddress("glClientActiveTextureARB");

    // get fragment and vertex program function pointers
    pGlGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) wglGetProcAddress("glGenProgramsARB");
    pGlGenProgramsNV = (PFNGLGENPROGRAMSNVPROC) wglGetProcAddress("glGenProgramsNV");
    pGlBindProgramARB = (PFNGLBINDPROGRAMARBPROC) wglGetProcAddress("glBindProgramARB");
    pGlBindProgramNV = (PFNGLBINDPROGRAMNVPROC) wglGetProcAddress("glBindProgramNV");
    pGlProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) wglGetProcAddress("glProgramStringARB");
    pGlLoadProgramNV = (PFNGLLOADPROGRAMNVPROC) wglGetProcAddress("glLoadProgramNV");
    pGlDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) wglGetProcAddress("glDeleteProgramsARB");
    pGlDeleteProgramsNV = (PFNGLDELETEPROGRAMSNVPROC) wglGetProcAddress("glDeleteProgramsNV");
    pGlProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) wglGetProcAddress("glProgramLocalParameter4fvARB");
    pGlCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) wglGetProcAddress("glCreateShaderObjectARB");
    pGlShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) wglGetProcAddress("glShaderSourceARB");
    pGlCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) wglGetProcAddress("glCompileShaderARB");
    pGlCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) wglGetProcAddress("glCreateProgramObjectARB");
    pGlAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) wglGetProcAddress("glAttachObjectARB");
    pGlLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) wglGetProcAddress("glLinkProgramARB");
    pGlUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) wglGetProcAddress("glUseProgramObjectARB");
    pGlDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) wglGetProcAddress("glDeleteObjectARB");
    pGlGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC) wglGetProcAddress("glGetAttachedObjectsARB");
    pGlGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) wglGetProcAddress("glGetInfoLogARB");
    pGlGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) wglGetProcAddress("glGetObjectParameterivARB");
    pGlGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) wglGetProcAddress("glGetUniformLocationARB");
    pGlUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) wglGetProcAddress("glUniform4fvARB");
    pGlUniform1ivARB = (PFNGLUNIFORM1IVARBPROC) wglGetProcAddress("glUniform1ivARB");
    pGlUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) wglGetProcAddress("glUniform1fvARB");
    pGlUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) wglGetProcAddress("glUniform2fvARB");
    pGlUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) wglGetProcAddress("glUniform3fvARB");
    pGlUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC) wglGetProcAddress("glUniformMatrix2fvARB");
    pGlUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC) wglGetProcAddress("glUniformMatrix3fvARB");
    pGlUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC) wglGetProcAddress("glUniformMatrix4fvARB");
    pGlGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC) wglGetProcAddress("glGetActiveUniformARB");

    // get point parameter extension
    pGlPointParameterfARB = (PFNGLPOINTPARAMETERFARBPROC) wglGetProcAddress("glPointParameterfARB");
    pGlPointParameterfvARB = (PFNGLPOINTPARAMETERFVARBPROC) wglGetProcAddress("glPointParameterfvARB");

    // get stencil extension
    pGlStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) wglGetProcAddress("glStencilFuncSeparate");
    pGlStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) wglGetProcAddress("glStencilOpSeparate");
    pGlStencilFuncSeparateATI = (PFNGLSTENCILFUNCSEPARATEATIPROC) wglGetProcAddress("glStencilFuncSeparateATI");
    pGlStencilOpSeparateATI = (PFNGLSTENCILOPSEPARATEATIPROC) wglGetProcAddress("glStencilOpSeparateATI");

    // compressed textures
    pGlCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) wglGetProcAddress("glCompressedTexImage2D");

    // FrameBufferObjects
    pGlBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC) wglGetProcAddress("glBindFramebufferEXT");
    pGlDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC) wglGetProcAddress("glDeleteFramebuffersEXT");
    pGlGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC) wglGetProcAddress("glGenFramebuffersEXT");
    pGlCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) wglGetProcAddress("glCheckFramebufferStatusEXT");
    pGlFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) wglGetProcAddress("glFramebufferTexture2DEXT");
    pGlBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC) wglGetProcAddress("glBindRenderbufferEXT");
    pGlDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC) wglGetProcAddress("glDeleteRenderbuffersEXT");
    pGlGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC) wglGetProcAddress("glGenRenderbuffersEXT");
    pGlRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC) wglGetProcAddress("glRenderbufferStorageEXT");
    pGlFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) wglGetProcAddress("glFramebufferRenderbufferEXT");
    pGlDrawBuffersARB = (PFNGLDRAWBUFFERSARBPROC) wglGetProcAddress("glDrawBuffersARB");
    pGlDrawBuffersATI = (PFNGLDRAWBUFFERSATIPROC) wglGetProcAddress("glDrawBuffersATI");

    // get vertex buffer extension
    pGlGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
    pGlBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
    pGlBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
    pGlDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB");
    pGlBufferSubDataARB= (PFNGLBUFFERSUBDATAARBPROC) wglGetProcAddress("glBufferSubDataARB");
    pGlGetBufferSubDataARB= (PFNGLGETBUFFERSUBDATAARBPROC)wglGetProcAddress("glGetBufferSubDataARB");
    pGlMapBufferARB= (PFNGLMAPBUFFERARBPROC) wglGetProcAddress("glMapBufferARB");
    pGlUnmapBufferARB= (PFNGLUNMAPBUFFERARBPROC) wglGetProcAddress("glUnmapBufferARB");
    pGlIsBufferARB= (PFNGLISBUFFERARBPROC) wglGetProcAddress("glIsBufferARB");
    pGlGetBufferParameterivARB= (PFNGLGETBUFFERPARAMETERIVARBPROC) wglGetProcAddress("glGetBufferParameterivARB");
    pGlGetBufferPointervARB= (PFNGLGETBUFFERPOINTERVARBPROC) wglGetProcAddress("glGetBufferPointervARB");
    pGlProvokingVertexARB= (PFNGLPROVOKINGVERTEXPROC) wglGetProcAddress("glProvokingVertex");
    pGlProvokingVertexEXT= (PFNGLPROVOKINGVERTEXEXTPROC) wglGetProcAddress("glProvokingVertexEXT");
    pGlColorMaskIndexedEXT= (PFNGLCOLORMASKINDEXEDEXTPROC) wglGetProcAddress("glColorMaskIndexedEXT");
    pGlEnableIndexedEXT= (PFNGLENABLEINDEXEDEXTPROC) wglGetProcAddress("glEnableIndexedEXT");
    pGlDisableIndexedEXT= (PFNGLDISABLEINDEXEDEXTPROC) wglGetProcAddress("glDisableIndexedEXT");
    pGlBlendFuncIndexedAMD= (PFNGLBLENDFUNCINDEXEDAMDPROC) wglGetProcAddress("glBlendFuncIndexedAMD");
    pGlBlendFunciARB= (PFNGLBLENDFUNCIPROC) wglGetProcAddress("glBlendFunciARB");
    pGlProgramParameteriARB= (PFNGLPROGRAMPARAMETERIARBPROC) wglGetProcAddress("glProgramParameteriARB");
    pGlProgramParameteriEXT= (PFNGLPROGRAMPARAMETERIEXTPROC) wglGetProcAddress("glProgramParameteriEXT");


#elif defined(_LYNX_LINUX_)
    // Accessing the correct function is quite complex
    // All libraries should support the ARB version, however
    // since GLX 1.4 the non-ARB version is the official one
    // So we have to check the runtime environment and
    // choose the proper symbol
    // In case you still have problems please enable the
    // next line by uncommenting it
    // #define _LYNX_GETPROCADDRESS_WORKAROUND_

#ifndef _LYNX_GETPROCADDRESS_WORKAROUND_
    __GLXextFuncPtr(*LYNX_OGL_LOAD_EXTENSION)(const GLubyte*)=0;
#ifdef GLX_VERSION_1_4
    int major=0,minor=0;
    if(glXGetCurrentDisplay())
        glXQueryVersion(glXGetCurrentDisplay(), &major, &minor);
    if((major>1) || (minor>3))
        LYNX_OGL_LOAD_EXTENSION=glXGetProcAddress;
    else
#endif
        LYNX_OGL_LOAD_EXTENSION=glXGetProcAddressARB;
#else
#define LYNX_OGL_LOAD_EXTENSION glXGetProcAddressARB
#endif

    pGlActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC)
                          LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glActiveTextureARB"));

    pGlClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC)
                                LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glClientActiveTextureARB"));

    // get fragment and vertex program function pointers
    pGlGenProgramsARB = (PFNGLGENPROGRAMSARBPROC)
                        LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGenProgramsARB"));

    pGlGenProgramsNV = (PFNGLGENPROGRAMSNVPROC)
                       LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGenProgramsNV"));

    pGlBindProgramARB = (PFNGLBINDPROGRAMARBPROC)
                        LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glBindProgramARB"));

    pGlBindProgramNV = (PFNGLBINDPROGRAMNVPROC)
                       LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glBindProgramNV"));

    pGlDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC)
                           LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glDeleteProgramsARB"));

    pGlDeleteProgramsNV = (PFNGLDELETEPROGRAMSNVPROC)
                          LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glDeleteProgramsNV"));

    pGlProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC)
                          LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glProgramStringARB"));

    pGlLoadProgramNV = (PFNGLLOADPROGRAMNVPROC)
                       LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glLoadProgramNV"));

    pGlProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC)
                                     LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glProgramLocalParameter4fvARB"));

    pGlCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)
                               LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glCreateShaderObjectARB"));

    pGlShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)
                         LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glShaderSourceARB"));

    pGlCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)
                          LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glCompileShaderARB"));

    pGlCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)
                                LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glCreateProgramObjectARB"));

    pGlAttachObjectARB = (PFNGLATTACHOBJECTARBPROC)
                         LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glAttachObjectARB"));

    pGlLinkProgramARB = (PFNGLLINKPROGRAMARBPROC)
                        LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glLinkProgramARB"));

    pGlUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC)
                             LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glUseProgramObjectARB"));

    pGlDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC)
                         LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glDeleteObjectARB"));

    pGlGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC)
                               LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGetAttachedObjectsARB"));

    pGlGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)
                       LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGetInfoLogARB"));

    pGlGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)
                                 LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGetObjectParameterivARB"));

    pGlGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC)
                               LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGetUniformLocationARB"));

    pGlUniform4fvARB = (PFNGLUNIFORM4FVARBPROC)
                       LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glUniform4fvARB"));

    pGlUniform1ivARB = (PFNGLUNIFORM1IVARBPROC)
                       LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glUniform1ivARB"));

    pGlUniform1fvARB = (PFNGLUNIFORM1FVARBPROC)
                       LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glUniform1fvARB"));

    pGlUniform2fvARB = (PFNGLUNIFORM2FVARBPROC)
                       LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glUniform2fvARB"));

    pGlUniform3fvARB = (PFNGLUNIFORM3FVARBPROC)
                       LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glUniform3fvARB"));

    pGlUniform4fvARB = (PFNGLUNIFORM4FVARBPROC)
                       LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glUniform4fvARB"));

    pGlUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC)
                             LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glUniformMatrix2fvARB"));

    pGlUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC)
                             LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glUniformMatrix3fvARB"));

    pGlUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC)
                             LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glUniformMatrix4fvARB"));

    pGlGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC)
                             LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGetActiveUniformARB"));

    // get point parameter extension
    pGlPointParameterfARB = (PFNGLPOINTPARAMETERFARBPROC)
                            LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glPointParameterfARB"));
    pGlPointParameterfvARB = (PFNGLPOINTPARAMETERFVARBPROC)
                             LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glPointParameterfvARB"));

    // get stencil extension
    pGlStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)
                             LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glStencilFuncSeparate"));
    pGlStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)
                           LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glStencilOpSeparate"));
    pGlStencilFuncSeparateATI = (PFNGLSTENCILFUNCSEPARATEATIPROC)
                                LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glStencilFuncSeparateATI"));
    pGlStencilOpSeparateATI = (PFNGLSTENCILOPSEPARATEATIPROC)
                              LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glStencilOpSeparateATI"));

    // compressed textures
    pGlCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)
                              LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glCompressedTexImage2D"));

#if defined(GLX_SGI_swap_control)
    // get vsync extension
    glxSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glXSwapIntervalSGI"));
#endif

#if defined(GLX_EXT_swap_control)
    // get vsync extension
    glxSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glXSwapIntervalEXT"));
#endif

    // FrameBufferObjects
    pGlBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)
                            LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glBindFramebufferEXT"));

    pGlDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
                               LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glDeleteFramebuffersEXT"));

    pGlGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)
                            LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGenFramebuffersEXT"));

    pGlCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
                                   LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glCheckFramebufferStatusEXT"));

    pGlFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
                                 LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glFramebufferTexture2DEXT"));

    pGlBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC)
                             LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glBindRenderbufferEXT"));

    pGlDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC)
                                LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glDeleteRenderbuffersEXT"));

    pGlGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC)
                             LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGenRenderbuffersEXT"));

    pGlRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC)
                                LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glRenderbufferStorageEXT"));

    pGlFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)
                                    LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glFramebufferRenderbufferEXT"));

    pGlDrawBuffersARB = (PFNGLDRAWBUFFERSARBPROC)
                        LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glDrawBuffersARB"));

    pGlDrawBuffersATI = (PFNGLDRAWBUFFERSATIPROC)
                        LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glDrawBuffersATI"));

    pGlGenBuffersARB = (PFNGLGENBUFFERSARBPROC)
                       LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGenBuffersARB"));

    pGlBindBufferARB = (PFNGLBINDBUFFERARBPROC)
                       LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glBindBufferARB"));

    pGlBufferDataARB = (PFNGLBUFFERDATAARBPROC)
                       LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glBufferDataARB"));

    pGlDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)
                          LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glDeleteBuffersARB"));

    pGlBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)
                          LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glBufferSubDataARB"));

    pGlGetBufferSubDataARB = (PFNGLGETBUFFERSUBDATAARBPROC)
                             LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGetBufferSubDataARB"));

    pGlMapBufferARB = (PFNGLMAPBUFFERARBPROC)
                      LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glMapBufferARB"));

    pGlUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)
                        LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glUnmapBufferARB"));

    pGlIsBufferARB = (PFNGLISBUFFERARBPROC)
                     LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glIsBufferARB"));

    pGlGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)
                                 LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGetBufferParameterivARB"));

    pGlGetBufferPointervARB = (PFNGLGETBUFFERPOINTERVARBPROC)
                              LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGetBufferPointervARB"));
    pGlProvokingVertexARB= (PFNGLPROVOKINGVERTEXPROC)
                           LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glProvokingVertex"));
    pGlProvokingVertexEXT= (PFNGLPROVOKINGVERTEXEXTPROC)
                           LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glProvokingVertexEXT"));
    pGlColorMaskIndexedEXT= (PFNGLCOLORMASKINDEXEDEXTPROC)
                            LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glColorMaskIndexedEXT"));
    pGlEnableIndexedEXT= (PFNGLENABLEINDEXEDEXTPROC)
                         LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glEnableIndexedEXT"));
    pGlDisableIndexedEXT= (PFNGLDISABLEINDEXEDEXTPROC)
                          LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glDisableIndexedEXT"));
    pGlBlendFuncIndexedAMD= (PFNGLBLENDFUNCINDEXEDAMDPROC)
                            LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glBlendFuncIndexedAMD"));
    pGlBlendFunciARB= (PFNGLBLENDFUNCIPROC)
                      LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glBlendFunciARB"));
    pGlProgramParameteriARB = (PFNGLPROGRAMPARAMETERIARBPROC)
                              LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glProgramParameteriARB"));
    pGlProgramParameteriEXT = (PFNGLPROGRAMPARAMETERIEXTPROC)
                              LYNX_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glProgramParameteriEXT"));

#endif // _IRR_WINDOWS_API_

    GLint num;
    // set some properties
    if(mVersion>102 || mFeatureAvailable[LYNX_ARB_multitexture])
    {
        glGetIntegerv(GL_MAX_TEXTURE_UNITS, &num);
        mMaxTextureUnits = static_cast<uint8_t>(num);
    }
    glGetIntegerv(GL_MAX_LIGHTS, &num);
    mMaxLights=static_cast<uint8_t>(num);
    if(mFeatureAvailable[LYNX_EXT_texture_filter_anisotropic])
    {
        glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &num);
        mMaxAnisotropy=static_cast<uint8_t>(num);
    }
    if(mVersion>101)
    {
        glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &num);
        mMaxIndices=num;
    }
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &num);
    mMaxTextureSize=static_cast<uint32_t>(num);
    if(query_feature(EVMF_GEOMETRY_SHADER))
    {
        glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT, &num);
        mMaxGeometryVerticesOut=static_cast<uint32_t>(num);
    }
    if(mFeatureAvailable[LYNX_EXT_texture_lod_bias])
        glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS_EXT, &mMaxTextureLODBias);
    glGetIntegerv(GL_MAX_CLIP_PLANES, &num);
    mMaxUserClipPlanes=static_cast<uint8_t>(num);
    glGetIntegerv(GL_AUX_BUFFERS, &num);
    mMaxAuxBuffers=static_cast<uint8_t>(num);
    if(mFeatureAvailable[LYNX_ARB_draw_buffers])
    {
        glGetIntegerv(GL_MAX_DRAW_BUFFERS_ARB, &num);
        mMaxMultipleRenderTargets = static_cast<uint8_t>(num);
    }
    glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, mDimAliasedLine);
    glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, mDimAliasedPoint);
    glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, mDimSmoothedLine);
    glGetFloatv(GL_SMOOTH_POINT_SIZE_RANGE, mDimSmoothedPoint);

    if(mFeatureAvailable[LYNX_ARB_shading_language_100] || mVersion>=200)
    {
        glGetError(); // clean error buffer
#ifdef GL_SHADING_LANGUAGE_VERSION
        const GLubyte* shaderVersion = glGetString(GL_SHADING_LANGUAGE_VERSION);
#else
        const GLubyte* shaderVersion = glGetString(GL_SHADING_LANGUAGE_VERSION_ARB);
#endif
        if(glGetError() == GL_INVALID_ENUM)
            mShaderLanguageVersion = 100;
        else
        {
            const float sl_ver = atof(reinterpret_cast<const char*>(shaderVersion));
            mShaderLanguageVersion = static_cast<uint16_t>(Myth::Math::floor(sl_ver)*100+Myth::Math::round(Myth::Math::fract(sl_ver)*10.0f));
        }
    }

    if(!pGlActiveTextureARB || !pGlClientActiveTextureARB)
    {
        mMultiTextureExtension = false;
        mLogManager->log("Failed to load OpenGL's multitexture extension, proceeding without.", ELL_WARNING);
    }
    else if(mMaxTextureUnits < 2)
    {
        mMultiTextureExtension = false;
        mLogManager->log("Warning: OpenGL device only has one texture unit. Disabling multitexturing.", ELL_WARNING);
    }
    mMaxTextureUnits = Myth::Math::min(mMaxTextureUnits,static_cast<uint8_t>(8));

}

bool OpenGLExtensionHandler::query_feature(E_VIDEO_MANAGER_FEATURE feature) const
{
    switch(feature)
    {
    case EVMF_RENDER_TO_TARGET:
        return true;
    case EVMF_HARDWARE_TL:
        return true; // we cannot tell other things
    case EVMF_MULTITEXTURE:
        return mMultiTextureExtension;
    case EVMF_BILINEAR_FILTER:
        return true;
    case EVMF_MIP_MAP:
        return true;
    case EVMF_MIP_MAP_AUTO_UPDATE:
        return mFeatureAvailable[LYNX_SGIS_generate_mipmap];
    case EVMF_STENCIL_BUFFER:
        return mStencilBuffer;
    case EVMF_VERTEX_SHADER_1_1:
    case EVMF_ARB_VERTEX_PROGRAM_1:
        return mFeatureAvailable[LYNX_ARB_vertex_program] || mFeatureAvailable[LYNX_NV_vertex_program1_1];
    case EVMF_PIXEL_SHADER_1_1:
    case EVMF_PIXEL_SHADER_1_2:
    case EVMF_ARB_FRAGMENT_PROGRAM_1:
        return mFeatureAvailable[LYNX_ARB_fragment_program] || mFeatureAvailable[LYNX_NV_fragment_program];
    case EVMF_PIXEL_SHADER_2_0:
    case EVMF_VERTEX_SHADER_2_0:
    case EVMF_ARB_GLSL:
        return mFeatureAvailable[LYNX_ARB_shading_language_100] || (mVersion>=200);
    case EVMF_TEXTURE_NSQUARE:
        return true; // non-square is always supported
    case EVMF_TEXTURE_NPOT:
        // Some ATI cards seem to have only SW support in OpenGL 2.0
        // drivers if the extension is not exposed, so we skip this
        // extra test for now!
        // return (FeatureAvailable[IRR_ARB_texture_non_power_of_two]||Version>=200);
        return (mFeatureAvailable[LYNX_ARB_texture_non_power_of_two]);
    case EVMF_FRAMEBUFFER_OBJECT:
        return mFeatureAvailable[LYNX_EXT_framebuffer_object];
    case EVMF_VERTEX_BUFFER_OBJECT:
        return mFeatureAvailable[LYNX_ARB_vertex_buffer_object];
    case EVMF_COLOR_MASK:
        return true;
    case EVMF_ALPHA_TO_COVERAGE:
        return mFeatureAvailable[LYNX_ARB_multisample];
    case EVMF_GEOMETRY_SHADER:
        return mFeatureAvailable[LYNX_ARB_geometry_shader4] || mFeatureAvailable[LYNX_EXT_geometry_shader4] || mFeatureAvailable[LYNX_NV_geometry_program4] || mFeatureAvailable[LYNX_NV_geometry_shader4];
    case EVMF_MULTIPLE_RENDER_TARGETS:
        return mFeatureAvailable[LYNX_ARB_draw_buffers] || mFeatureAvailable[LYNX_ATI_draw_buffers];
    case EVMF_MRT_BLEND:
    case EVMF_MRT_COLOR_MASK:
        return mFeatureAvailable[LYNX_EXT_draw_buffers2];
    case EVMF_MRT_BLEND_FUNC:
        return mFeatureAvailable[LYNX_ARB_draw_buffers_blend] || mFeatureAvailable[LYNX_AMD_draw_buffers_blend];
    default:
        return false;
    };
}

} // namespace Lynx
